(*
 * PdP ML
 * 
 * Francesco Cotto 
 *
 * Es.5a Liste lazy in ML: Eratostene 
 *
 *)

type 'a llist = Lnull | Lcons of ( unit -> ('a * 'a llist) );;

exception ArgException of string ;;

let llhd = function Lnull    -> raise (ArgException "head su lista vuota")
                  | Lcons(y) -> fst (y()) ;;

let lltl = function Lnull    -> raise (ArgException "tail su lista vuota")
                  | Lcons(y) -> snd (y()) ;;                                           


(* solo con llhd e lltl *)
let rec lltake_1 len ll =
   match len with
        0   -> []
      | _   -> llhd ll :: lltake_1 (len-1) (lltl ll) ;;


(* con llhd e lltl *)
let rec lltake_2 len = function Lnull -> []
                              | ll -> let hd = llhd ll in
                              if (len > 0) then hd ::  lltake_2 (len-1) (lltl ll)
                                           else [] ;;
(* senza llhd e lltl (ok) *)
let rec lltake_3 len = function Lnull -> []
                              |Lcons (yl) -> let y = yl() in let hd = fst y in
                              if (len > 0) then hd ::  lltake_3 (len-1) (snd y)
                                           else [] ;;

(* semplice alias per evidenziare la versione "corretta" *)
let lltake = lltake_3 ;;

(* filtro multipli di n da lista lazy *)
let rec fltMlt n = function Lnull -> Lnull
                          | Lcons (yl) -> let y = yl() in let hd = fst y in 
                          if (hd mod n) = 0
                             then fltMlt n (snd y) 
                             else Lcons (function()->(hd, fltMlt n (snd y) ) );;

(* per debug *)
let cntCallErs = ref 0;;

(* generatore lazy list 1,2,3... *)
let rec from n = Lcons(function () -> (n, from (n+1))) ;;
let rec myFrom n = Lcons(function ()-> print_int n; print_string"."; (n,myFrom(n+1)));;


let rec eratostene = function Lnull -> Lnull
                            | Lcons(yl) -> let y = yl() in 
                                           let hd = fst y in 
                                           let lf = fltMlt hd (snd y) in 
                                           cntCallErs:=(!cntCallErs + 1); 
                                           Lcons (function () -> (hd, eratostene lf) );;

                                         
let primes n ftake =  cntCallErs := 0; ftake n (eratostene (from 2) );;



primes 20 lltake_1;;
print_string ("[lltake_1] # chiamate ricorsive eratostene: "^(string_of_int
!cntCallErs)^"\n\n");;


primes 20 lltake_2;;
print_string ("[lltake_2] # chiamate ricorsive eratostene: "^(string_of_int
!cntCallErs)^"\n\n");;


primes 20 lltake_3;;
print_string ("[lltake_3] # chiamate ricorsive eratostene: "^(string_of_int
!cntCallErs)^"\n\n");;


(* esempi di utilizzo: 
 * 
 *  lltake 100 (eratostene (from 2));;
 *  
 *  lltake 100 (eratostene (myFrom 2));;
 *   
 *  let ll = from 2;; 
 *  match ll with Lcons y -> fst (y()) ;;
 *    
 * *)
